// Copyright 2022 Tencent
#include "base/function.h"
#include "util/mempool.h"
#include "base/log.h"

#include <memory.h>
#include <stdlib.h>
#include <iostream>

namespace net_tools::util
{
  namespace detail
  {
    struct Memorypool_point;
  }
#define to_char(x) reinterpret_cast<char *>(x)
#define to_memptr(x) reinterpret_cast<detail::Memorypool_point *>(x)
#define MEMORY_POOL_POINT_SIZE sizeof(detail::Memorypool_point)

  Memorypool::Memorypool(size_t block_size, size_t block_number)
      : pmemory_data_(nullptr),
        pmemory_pool_(nullptr),
        block_number_(block_number),
        block_size_(block_size),
        block_size_move_(0),
        mutex_()
  {
    Log::Init();
    while (block_size_)
    {
      ++block_size_move_;
      block_size_ >>= 1;
    }
    --block_size_move_;
    NT_LOG_INFO << "block_size_move_: " << block_size_move_ << NT_LOG_ENDL;
    block_size_ = block_size;
    memory_pool_size_ = (block_number_ * (block_size_ + MEMORY_POOL_POINT_SIZE));
    memory_pool_data_size_ = block_number_ * block_size_;

    pmemory_pool_ = malloc(memory_pool_size_);
    pmemory_data_ = to_char(pmemory_pool_) + block_number_ * MEMORY_POOL_POINT_SIZE;
    if (pmemory_pool_ == nullptr)
    {
      std::cout << "内存池初始错误 malloc错误!!!!!" << std::endl;
      exit(0);
    }
    else
    {
      memset(pmemory_pool_, 0, memory_pool_size_);

      help_point_head_ = new detail::Memorypool_point;
      help_point_back_ = new detail::Memorypool_point;
      help_point_head_->next_ = help_point_back_;
      help_point_back_->before_ = help_point_head_;

      detail::Memorypool_point *pfirst_memory_pool_point =
          Point_create(to_memptr(pmemory_pool_), memory_pool_data_size_); //初始化整个链表

      Point_insert(help_point_head_, pfirst_memory_pool_point); //将初始大区块插入链表中
      Point_back_create(pfirst_memory_pool_point);
      free_block_number_ = 1;
      pmemory_data_end_ = to_char(pmemory_data_) + memory_pool_data_size_;
      NT_LOG_INFO << "内存池初始成功 size:" << memory_pool_size_ << NT_LOG_ENDL;
    }
  };
  Memorypool::~Memorypool()
  {
    free(pmemory_pool_);
  };

  void *Memorypool::Get_memory_unlocked(size_t block_number)
  {
    if (free_block_number_ == 0) [[unlikely]]
      return nullptr;                                        //没有空block了
    detail::Memorypool_point *tmp = help_point_head_->next_; //为第一个真实可用节点
    size_t need_size = block_number * block_size_;
    while (tmp->next_->size_ != 0)
    { //不等于最后的帮助节点
      if (tmp->size_ >= need_size)
        break; //该节点大小大于所需内存
      tmp = tmp->next_;
    }
    if (tmp->size_ < need_size) [[unlikely]]
      return nullptr; //head->next_为帮助节点且当前head小于size 申请失败

    if (tmp->size_ == need_size)
    { //相等 则不用分割
      Point_cut(tmp);
      Point_back_create(tmp);
      --free_block_number_;
    }
    else
    {
      //把多出来的块 建立记录块
      detail::Memorypool_point *back = Point_create(to_char(tmp) + block_number * MEMORY_POOL_POINT_SIZE, tmp->size_ - need_size);
      //用back代替tmp
      back->before_ = tmp->before_;
      back->before_->next_ = back;
      back->next_ = tmp->next_;
      back->next_->before_ = back;
      if (back->size_ != block_size_)
        Point_back_create(back);
      Point_create(tmp, need_size);
    }
    return Change_block_to_data(tmp);
  };
  void *Memorypool::Get_memory_locked(size_t block_number)
  {
    mutex_.Lock();
    if (free_block_number_ == 0) [[unlikely]]
    {
      mutex_.Unlock();
      return nullptr; //没有空block了
    }
    detail::Memorypool_point *tmp = help_point_head_->next_; //为第一个真实可用节点
    size_t need_size = block_number * block_size_;
    while (tmp->next_->size_ != 0)
    { //不等于最后的帮助节点
      if (tmp->size_ >= need_size)
        break; //该节点大小大于所需内存
      tmp = tmp->next_;
    }
    if (tmp->size_ < need_size) [[unlikely]]
    {
      mutex_.Unlock();
      return nullptr; //head->next_为帮助节点且当前head小于size 申请失败
    }
    if (tmp->size_ == need_size)
    { //相等 则不用分割
      Point_cut(tmp);
      Point_back_create(tmp);
      --free_block_number_;
    }
    else
    {
      //把多出来的块 建立记录块
      detail::Memorypool_point *back = Point_create(to_char(tmp) + block_number * MEMORY_POOL_POINT_SIZE, tmp->size_ - need_size);
      //用back代替tmp
      back->before_ = tmp->before_;
      back->before_->next_ = back;
      back->next_ = tmp->next_;
      back->next_->before_ = back;
      if (back->size_ != block_size_)
        Point_back_create(back);
      Point_create(tmp, need_size);
    }
    mutex_.Unlock();
    return Change_block_to_data(tmp);
  };

  void Memorypool::Free_memory_unlocked(void *paddress)
  {
    if (paddress < pmemory_data_ || paddress > (to_char(pmemory_data_) + memory_pool_data_size_)) [[unlikely]]
    {
      NT_LOG_ERROR << "free memory 越界" << NT_LOG_ENDL;
      return;
    }

    detail::Memorypool_point *block = to_memptr(Change_data_to_block(paddress));
    detail::Memorypool_point *before = nullptr;
    detail::Memorypool_point *next = nullptr;

    if (block != pmemory_pool_)
    {                                                              //如果不是队首 则有上一个节点
      before = to_memptr(to_char(block) - MEMORY_POOL_POINT_SIZE); //前一个节点尾部信息
      if (before->before_ != nullptr)
      {                                                                                                    //如果前一个节点可以合并
        before = to_memptr(to_char(block) - (before->size_ >> block_size_move_) * MEMORY_POOL_POINT_SIZE); //头
        before->size_ += block->size_;
        block = before;
        Point_back_create(block);
      }
      else
      {
        before = nullptr;
      }
    }

    if (to_char(paddress) + block->size_ != pmemory_data_end_)
    {                                                                                                 //如果不是最后一个
      next = to_memptr(to_char(block) + (block->size_ >> block_size_move_) * MEMORY_POOL_POINT_SIZE); //后一个节点信息
      if (next->next_ != nullptr)
      { //如果后一个节点可以合并 且不是帮助节点
        if (block->before_ != nullptr)
        { //如果和前一个节点合并过了 记录块整理好了
          block->size_ += next->size_;
          block->next_ = next->next_;
          block->next_->before_ = block;
          Point_back_create(block);
          Point_back_create(block->next_); //修改后面节点的尾部数据块 当后面节点不是帮助节点的时候
          --free_block_number_;
        }
        else
        { //如果没有合并过 没有指向前的指针
          block->size_ += next->size_;
          block->before_ = next->before_;
          block->before_->next_ = block;
          block->next_ = next->next_;
          block->next_->before_ = block;
          Point_back_create(block);
          Point_back_create(block->before_);
          Point_back_create(block->next_);
        }
      }
      else
      {
        next = nullptr;
      }
    }

    if (next == nullptr && before == nullptr)
    { //如果前后都被占用 则从链表中寻找可用连接
      if (free_block_number_ == 0)
      { //如果没有空闲节点
        Point_insert(help_point_head_, block);
      }
      else
      {
        detail::Memorypool_point *tmp_before = help_point_head_->next_;
        for (size_t count = 1; count < block_number_ && tmp_before < block; ++count)
        {
          tmp_before = tmp_before->next_;
        }
        if (tmp_before > block)
        {
          Point_insert(tmp_before->before_, block);
        }
        else
        {
          Point_insert(tmp_before, block);
        }
      }
      Point_back_create(block);
      if (block->before_->size_ != 0)
        Point_back_create(block->before_);
      if (block->next_->size_ != 0)
        Point_back_create(block->next_);
      ++free_block_number_;
    }
  };
  void Memorypool::Free_memory_locked(void *paddress)
  {
    if (paddress < pmemory_data_ || paddress > (to_char(pmemory_data_) + memory_pool_data_size_)) [[unlikely]]
    {
      NT_LOG_ERROR << "free memory 越界" << NT_LOG_ENDL;
      return;
    }

    detail::Memorypool_point *block = to_memptr(Change_data_to_block(paddress));

    detail::Memorypool_point *before = nullptr;
    detail::Memorypool_point *next = nullptr;

    mutex_.Lock();
    if (block != pmemory_pool_)
    {                                                              //如果不是队首 则有上一个节点
      before = to_memptr(to_char(block) - MEMORY_POOL_POINT_SIZE); //前一个节点尾部信息
      if (before->before_ != nullptr)
      {                                                                                                    //如果前一个节点可以合并
        before = to_memptr(to_char(block) - (before->size_ >> block_size_move_) * MEMORY_POOL_POINT_SIZE); //头
        before->size_ += block->size_;
        block = before;
        Point_back_create(block);
      }
      else
      {
        before = nullptr;
      }
    }

    if (to_char(paddress) + block->size_ != pmemory_data_end_)
    {                                                                                                 //如果不是最后一个
      next = to_memptr(to_char(block) + (block->size_ >> block_size_move_) * MEMORY_POOL_POINT_SIZE); //后一个节点信息
      if (next->next_ != nullptr)
      { //如果后一个节点可以合并 且不是帮助节点
        if (block->before_ != nullptr)
        { //如果和前一个节点合并过了 记录块整理好了
          block->size_ += next->size_;
          block->next_ = next->next_;
          block->next_->before_ = block;
          Point_back_create(block);
          Point_back_create(block->next_); //修改后面节点的尾部数据块 当后面节点不是帮助节点的时候
          --free_block_number_;
        }
        else
        { //如果没有合并过 没有指向前的指针
          block->size_ += next->size_;
          block->before_ = next->before_;
          block->before_->next_ = block;
          block->next_ = next->next_;
          block->next_->before_ = block;
          Point_back_create(block);
          Point_back_create(block->before_);
          Point_back_create(block->next_);
        }
      }
      else
      {
        next = nullptr;
      }
    }

    if (next == nullptr && before == nullptr)
    { //如果前后都被占用 则从链表中寻找可用连接
      if (free_block_number_ == 0)
      { //如果没有空闲节点
        Point_insert(help_point_head_, block);
      }
      else
      {
        detail::Memorypool_point *tmp_before = help_point_head_->next_;
        for (size_t count = 1; count < block_number_ && tmp_before < block; ++count)
        {
          tmp_before = tmp_before->next_;
        }
        if (tmp_before > block)
        {
          Point_insert(tmp_before->before_, block);
        }
        else
        {
          Point_insert(tmp_before, block);
        }
      }
      Point_back_create(block);
      if (block->before_->size_ != 0)
        Point_back_create(block->before_);
      if (block->next_->size_ != 0)
        Point_back_create(block->next_);
      ++free_block_number_;
    }
    mutex_.Unlock();
  };

  void *Memorypool::Change_block_to_data(detail::Memorypool_point *address)
  {
    return ((to_char(address) - to_char(pmemory_pool_)) / MEMORY_POOL_POINT_SIZE) *
               block_size_ +
           to_char(pmemory_data_);
  };
  void *Memorypool::Change_data_to_block(void *address)
  {
    return ((to_char(address) - to_char(pmemory_data_)) >> block_size_move_) *
               MEMORY_POOL_POINT_SIZE +
           to_char(pmemory_pool_);
  };
  void Memorypool::Point_insert(detail::Memorypool_point *pbefore, detail::Memorypool_point *pmemory_pool_point)
  {
    pmemory_pool_point->next_ = pbefore->next_;
    pmemory_pool_point->before_ = pbefore;
    pbefore->next_ = pmemory_pool_point;
    pmemory_pool_point->next_->before_ = pmemory_pool_point;
  };
  void Memorypool::Point_cut(detail::Memorypool_point *pmemory_pool_point)
  {
    pmemory_pool_point->next_->before_ = pmemory_pool_point->before_;
    pmemory_pool_point->before_->next_ = pmemory_pool_point->next_;
    pmemory_pool_point->before_ = nullptr;
    pmemory_pool_point->next_ = nullptr;
  };
  detail::Memorypool_point *Memorypool::Point_create(void *paddress, const size_t size)
  {
    detail::Memorypool_point *pback_pool_point = to_memptr((to_char(paddress) +
                                                            (size >> block_size_move_) *
                                                                MEMORY_POOL_POINT_SIZE -
                                                            MEMORY_POOL_POINT_SIZE)); //要包括的记录块的最后一个
    //把这一整块记录块最前面和最后面记录成占用
    pback_pool_point->size_ = size;
    pback_pool_point->next_ = nullptr;
    pback_pool_point->before_ = nullptr;
    detail::Memorypool_point *pmemory_pool_point = to_memptr(paddress); //头point
    pmemory_pool_point->size_ = size;
    pmemory_pool_point->next_ = nullptr;
    pmemory_pool_point->before_ = nullptr;
    return pmemory_pool_point;
  };
  void Memorypool::Point_back_create(detail::Memorypool_point *paddress)
  {
    detail::Memorypool_point *pback_pool_point = to_memptr((to_char(paddress) +
                                                            (paddress->size_ >> block_size_move_) *
                                                                MEMORY_POOL_POINT_SIZE -
                                                            MEMORY_POOL_POINT_SIZE));
    pback_pool_point->size_ = paddress->size_;
    pback_pool_point->next_ = paddress->next_;
    pback_pool_point->before_ = paddress->before_;
  };

} // namespace net_tools::base